home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-08-08 | 32.6 KB | 1,204 lines | [TEXT/R*ch] |
- /*
- $Header: gio.c,v 1.19 90/02/28 04:15:27 gnu Exp $
- Copyright 1990 Free Software Foundation, Inc.x
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 1, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-
- "G-protocol" module for error-checked serial communications for uucp.
-
- Derived from:
- uuslave.c 1.7 08/12/85 14:04:20
- which came from the ACGNJ BBS system at +1 201 753 9758. Original
- author unknown.
- */
-
- #ifndef lint
- char gio_version[] = "Version Fort Pond Research-6.12";
- #endif
-
- #include "includes.h" /* All the system header files */
- #include "uucp.h"
- #include "sysdep.h"
-
- extern int errno;
-
- int connection_closing = FALSE;
- /*
-
-
- This program implements the uucp (Unix-to-Unix CoPy) protocol as a
- slave (the recipient of a phone call from another Unix system) or, now,
- a master (the originator of a phone call). This
- protocol is used to transfer mail, files, and Usenet news from Unix
- machine to Unix machine. UUCP comes with Unix (unless you get a
- sleazeoid version like Xenix, where they charge you extra for it). You
- can buy a commercial program for MSDOS, called UULINK, which also
- implements this protocol. UULINK costs $300 and you don't get sources,
- though.
-
- The protocol requires a full 8-bit data path with no characters inserted
- or deleted (e.g. ^S and ^Q are used as DATA characters). Simple serial
- ports and modems do this; most complicated networks do not, at least without
- setting up odd modes and such. Telenet's PC Pursuit works though.
-
- The basic flow of the protocol is that the calling machine will send down
- a line of text saying what it wants to do (send a file, receive a file,
- or hang up). (The lines of text are encapsulated into packets; see below.)
- The called machine responds with a "yes" or "no" answer, and if the answer
- was yes, it sends or receives the file. Files are terminated with a
- packet containing 0 bytes of data. Then the system that received the file
- sends a "copy succeeded" or "copy failed" line to the other end, and
- they go back to "what do we do now". A request to hang up should be
- answered "no" if the called machine has some mail or files it wants to
- send to the calling machine; the two machines reverse roles and the calling
- machine goes into "what do we do now". If a hangup request is answered "yes",
- the call is terminated.
-
- The data flow described above is actually sent in packets containing
- checksums and acknowledgements. Each packet can either hold a short
- control message (e.g. an ack) or a data block. The data blocks are
- numbered with a 3-bit counter, and sent with a checksum. If the sender
- has not received an acknowledgement for a data block within a certain
- time, it retransmits the block. The size of a data block is negotiated
- at the start of a call. To send a block with fewer bytes, a "short
- data" block is sent, which is just as big as a "long data" block, but
- contains a 1- or 2-byte count of "how many bytes in this block are just
- padding". This is a cute trick since it always works (e.g. if you want
- to send 1023 out of 1024 bytes, you only need one byte for the count;
- while if you want to send 1 byte out of 1024 then you have enough space
- for the count to be 2 bytes).
-
- The short control messages are used to start the call and negotiate the
- packet size and the "window size", to acknowledge or reject packets,
- and to terminate the packet protocol at the end of a call. The window
- size is how many packets one side can send before it will stop and wait
- for an acknowledgement from the other side. A window size of 1 makes
- for a half-duplex protocol (which is what gio.c currently implements),
- but also makes it easy to implement on micros that don't handle serial
- lines with interrupts. In window 1, you just keep sending the same
- packet until the other side acknowledges it. Unix traditionally uses a
- window size of 3, because Greg Chesson didn't have an environment where
- a larger window did any good. Modern buffered (internally half duplex)
- modems and packet switching networks provide such an environment, so we
- offer a window size of 7, the maximum. This gives much better
- throughput, but requires full duplex serial port handling and more
- complicated acknowledgement strategies.
-
- At the low level, full 8-bit bytes are sent out and received on an async serial
- port. The received data is scanned for a DLE (hex 10) which indicates the
- start of a packet, and the next 5 bytes are read and checked. If they
- pass, this is a good packet and it is acted upon. (If it's a data
- packet, we have to read in and check the data part too.) If the checks
- fail, all the bytes read so far should be scanned for another DLE.
-
- FIXME: Think about how to do data transfers in BOTH directions
- simultaneously, without completely botching the structure of the
- code. We should be able to move news/mail both ways at once.
-
- FIXME: Check the ack strategy to make sure we are kicking the other
- side with acks whenever necessary.
-
- FIXME: Support some real timeouts for chars that are less than
- the send-expect timeouts and which don't kill the protocol.
- */
-
-
- /*
- * UUCP "g" procotol definitions
- */
- #define MAGIC 0125252 /* checksum is subtracted from this */
-
- /*
- * What is sent from one machine to the other is a control byte and
- * sometimes a data block following.
- * A packet is just this, with a frame around the control byte and the
- * data, if any, after the frame. The frame is 6 bytes long, and looks like:
- * DLE K C0 C1 C X
- * where:
- * DLE is a literal ASCII DLE (Data Link Escape) character
- * K is binary, 9 for a control packet, or the packet size log 2, minus 4
- * e.g. K = 2 means 64 byte packet since (K+4) is 6 or 64.
- * 2 2
- * C0 and C1 are low, high order checksums for the entire data section, or
- * are low, high order of (MAGIC minus the control byte).
- * C is the control byte for the message.
- * X is the xor of K, C0, C1, and C.
- * If a packet does not satisfy all of the above checks, it is invalid.
- */
- #define DLE 0x10 /* Start of packet indicator */
-
- #define DLEBYTE 0 /* Byte offset from DLE to DLE */
- #define LENBYTE 1 /* Byte offset from DLE to length */
- #define CHKBYTE1 2 /* Byte offset from DLE to checksum byte 1 */
- #define CHKBYTE2 3 /* Byte offset from DLE to checksum byte 2 */
- #define CBYTE 4 /* Byte offset from DLE to control */
- #define FRAMEBYTE 5 /* Byte offset from DLE to end of frame */
- #define XORBYTE 5 /* Byte offset from DLE to xor-checksum */
- #define SHORTLEN1 6 /* Byte offset from DLE to len of shortdata */
- #define SHORTLEN2 7 /* Byte offset from DLE to 2ndlen of shortdat */
-
- #define FRAMELEN 6 /* Length of whole frame (except data) */
-
- #define KCONTROL 9 /* K value for control packets */
-
- /*
- * A control byte is split into three bit fields: type, x, and y.
- * TT XXX YYY
- * Here are the types:
- */
- #define TYPESHIFT 6 /* Offset of TYPE in control byte */
- #define XXXSHIFT 3 /* Offset of XXX in control byte */
- #define YYYSHIFT 0 /* Offset of YYY in control byte */
-
- #define TYPEMASK 0x03 /* Mask for field size */
- #define XXXMASK 0x07
- #define YYYMASK 0x07
-
- #define CONTROL 0 /* Control message */
- #define ALTCHN 1 /* Alternate channel message, unused in UUCP */
- #define LONGDATA 2 /* Long data block -- full size spec'd by K */
- #define SHORTDATA 3 /* Short data block -- first byte or two is
- count. Full K-size packet is sent, even
- though the useful data is shorter. */
-
- char *tt_pr[] = {"CONTROL", "ALTCHN", "LONGDATA", "SHORTDATA"};
-
- /* If TT == CONTROL (also K will == KCONTROL) then
- the x field is: and the Y field means: */
- #define CLOSE 1 /* End of communication */
- #define RJ 2 /* Reject packet last good packet # seen */
- #define SRJ 3 /* Selective reject seq # of bad packet, resend
- SRJ is not used by UUCP. */
- #define RR 4 /* Receiver Ready last good packet # seen */
- #define INITC 5 /* Init phase C window size to hand sender */
- #define INITB 6 /* Init phase B max data segment size (K val)
- to hand sender */
- #define INITA 7 /* Init phase A window size to hand sender */
-
- char *ctrl_pr[] = {"*ZERO*",
- "CLOSE", "RJ", "SRJ", "RR", "INITC", "INITB", "INITA"};
-
- /*
- * If TT == LONGDATA or SHORTDATA then x field is the sequence # of this packet
- * and y field is the last good packet # seen.
- *
- * In both data and RJ/RR packets, the "last good packet # seen" starts off
- * as zero.
- */
-
-
- #define MAX_PACKET 64
-
- unsigned char msgi[MAX_PACKET+SLOP]; /* Incoming packet */
- unsigned char msgout[8][MAX_PACKET+SLOP]; /* Outgoing packets #0 to #7 */
- unsigned char ctlout[FRAMELEN]; /* Outgoing control packet */
-
- int msgsize, /* Size of data part of msg */
- tt, xxx, yyy, /* Fields from control byte */
- rseq, /* Last good packet # we received */
- his_rseq, /* Last good packet # HE received */
- wseq; /* Next packet # we will send */
-
- #define NOOP 0
- #define ACK_MSG 1
- #define RETRANS_MSG 2
-
- int need_write = NOOP; /* Flag: need to write (an ack or
- data packet) to let the other side
- know what we're up to */
-
- int reject; /* Packet # to reject or NOREJECT */
- #define NOREJECT -1
-
- /* Segments are encoded as (log2 length) - 3, except in INITB packet */
- /* FIXME window size */
- #define MAX_WINDOW 7
- #ifdef BUFFEREDIO
- int wndsiz = MAX_WINDOW; /* Ask for window of maximum # packets */
- #else
- int wndsiz = 1 /* Ask for window of 1 messages flying */
- #endif
- int segsiz = 2; /* Ask for 64 byte messages */
- int sendseg = 2; /* Size segments other guy wants to see */
- int sendwin = 1; /* Size window other guy wants to see */
-
- int sendbytes; /* sendseg, in bytes */
-
- #define s_or_f int
-
- void DEBUG_DATA (int data);
- void
- DEBUG_DATA(data)
- int data;
- {
- if (isprint((char)data))
- putchar((char)data);
- else
- putchar(' ');
- }
-
- int
- pktchksum(unsigned char *msg, int bytes);
- void
- update_rseq(unsigned char *msgo);
-
- static int segbytes[10] = { /* K value (encoded segment size) */
- -1, /* 0 */
- 32, /* 1 */
- 64, /* 2 */
- 128, /* 3 */
- 256, /* 4 */
- 512, /* 5 */
- 1024, /* 6 */
- 2048, /* 7 */
- 4096, /* 8 */
- 0, /* 9 = KCONTROL */
- };
-
- /* s_or_f inpkt();
- int pktchksum();
- int chksum(); */
-
-
- /*
- * Low level output routines. These send packets without checking
- * whether they got properly received.
- *
- * writeframe():
- *
- * Finish off an outgoing frame in msgo and queue it up to be written
- * to the serial port.
- *
- * This routine is called to send each and every packet.
- *
- * Note that with this setup, we may hang in the xwrite() below
- * looking for outgoing buffer space, even though there are input
- * characters (packets) that we could process if we would look for
- * them. This may want to be fixed FIXME.
- */
- s_or_f
- writeframe(cksm, msgo)
- int cksm;
- unsigned char *msgo;
- {
- int status, maxlen;
-
- msgo[DLEBYTE] = (unsigned char)DLE;
- msgo[CHKBYTE1] = (unsigned char)cksm;
- msgo[CHKBYTE2] = (unsigned char)(cksm >> 8);
- msgo[XORBYTE] = msgo[1] ^ msgo[2] ^ msgo[3] ^ msgo[4];
-
- maxlen = segbytes[msgo[LENBYTE]] + FRAMELEN;
-
- if (DEBUG_LEVEL(9)) {
- int i;
-
- printf("T ");
- for (i = 0; i < maxlen; i++)
- DEBUG_DATA(msgo[i]);
- putchar('\n');
- }
- if (DEBUG_LEVEL(6)) {
- int t, x, y;
-
- t = (msgo[CBYTE] >> TYPESHIFT) & TYPEMASK;
- x = (msgo[CBYTE] >> XXXSHIFT) & XXXMASK;
- y = (msgo[CBYTE] >> YYYSHIFT) & YYYMASK;
- if (t == CONTROL)
- printf("Sent: CONTROL %s %d\n",
- ctrl_pr[x], y);
- else
- printf("Sent: %s %d %d\n",
- tt_pr[t], x, y);
- }
-
- /* Actually write it out the serial port */
- {
- char orig;
- char did_it = 0;
- int rand_val = rand();
- int type_val = (msgo[CBYTE] >> TYPESHIFT) & TYPEMASK;
- if ((type_val == LONGDATA) &&
- rand_val <= (int)0)
- {
- did_it = 1;
- orig = msgo[10];
- msgo[10] = 44;
- }
- status = xwrite((char *)msgo, maxlen);
- if (did_it == 1)
- {
- msgo[10] = orig;
- did_it = 0;
- }
- }
- if (status != maxlen) {
- if (DEBUG_LEVEL(0)) {
- printf("xmit %d bytes failed, status %d, errno %d\n",
- maxlen, status, errno);
- }
- return FAIL; /* Write failed */
- }
-
- need_write = NOOP;
- return SUCCESS;
- }
-
-
- /*
- * Send an ack. If the ack would be for packet N and we have seen a garbled
- * copy of the next packet, send a reject acknowledging packet N rather than a
- * regular acknowledgement of packet N. It's not clear what uucp's strategy
- * for this is, let's see how this one does. FIXME.
- */
- s_or_f
- ackmsg()
- {
-
- if (connection_closing == TRUE) return(SUCCESS);
- ctlout[LENBYTE] = (unsigned char)KCONTROL;
- if (reject == rseq)
- ctlout[CBYTE] = (unsigned char)((CONTROL << TYPESHIFT) |
- (RJ << XXXSHIFT) |
- (reject << YYYSHIFT) );
- else
- ctlout[CBYTE] = (unsigned char)((CONTROL << TYPESHIFT) |
- (RR << XXXSHIFT) |
- (rseq << YYYSHIFT) );
- reject = NOREJECT;
- return writeframe((int)(MAGIC - ctlout[CBYTE]), ctlout);
- }
-
-
- /* Send a control message other than an ack */
- s_or_f
- ctlmsg(byte)
- char byte;
- {
-
- ctlout[LENBYTE] = (unsigned char)KCONTROL;
- ctlout[CBYTE] = (unsigned char)((CONTROL << TYPESHIFT) | byte);
- return writeframe((int)(MAGIC - ctlout[CBYTE]), ctlout);
- }
-
-
- /*
- * Medium level output routine. This sends a short or long data packet
- * and figures out when to retransmit and/or insert acknowledgements as
- * needed.
- */
- s_or_f
- sendpacket(s, n, sorl)
- char *s;
- int n;
- int sorl; /* SHORTDATA or LONGDATA */
- {
- int offset, difflen;
- unsigned char *msgo;
-
- while (((wseq - his_rseq) & MAX_WINDOW) >= sendwin) {
- /*
- * Better get the earlier packet(s) sent and ack'd first
- * to make room for this one in the window.
- */
- if (inpkt() != SUCCESS)
- {
- return FAIL;
- }
- switch (tt) {
- default:
- unexp:
- DEBUG(0, "Unexpected pkt received in sendpacket\n", 0);
- return FAIL; /* Unsupported - yet */
-
- case CONTROL:
- switch (xxx) {
- default:
- goto unexp; /* Bad packet type */
-
- case RJ: /* Reject prev pkt */
- case RR: /* OK but no data */
- break; /* These are OK */
- }
- }
- }
-
- msgo = msgout[wseq]; /* Pick a place to put the datapacket */
-
- /* FIXME, is this bzero burning a lot of time? For completely full
- long-data packets, we can skip it. */
- bzero((char *)(msgo+FRAMELEN), sendbytes);
-
- /* FIXME, we might consider shrinking the send segment size to
- match the data size... dunno if this will break Unix uucp. */
- msgo[LENBYTE] = (unsigned char)sendseg;
- msgo[CBYTE] = (unsigned char)((sorl << TYPESHIFT) |
- (wseq << XXXSHIFT) |
- (rseq << YYYSHIFT) );
-
- switch(sorl) {
- case LONGDATA:
- default:
- if (n > sendbytes) abort();
- offset = FRAMELEN;
- break;
-
- case SHORTDATA:
- difflen = sendbytes - n;
- if (difflen < 1) abort();
- offset = FRAMELEN+1;
- if (difflen <= 127) {
- /* One byte count */
- msgo[SHORTLEN1] = (unsigned char)difflen;
- } else {
- /* low byte, with 0x80 on, then high byte */
- msgo[SHORTLEN1] = (unsigned char)(0x80 | difflen);
- msgo[SHORTLEN2] = (unsigned char)(difflen >> 7);
- offset = FRAMELEN+2;
- }
- }
-
- bcopy(s, (char *)(msgo+offset), n); /* Move the data */
-
- wseq = (wseq + 1) & MAX_WINDOW; /* Bump sent pkt sequence # */
- return writeframe(pktchksum(msgo, sendbytes), msgo);
- }
-
- /*
- * Update the received sequence number in packets that we retransmit.
- */
- void
- update_rseq(msgo)
- unsigned char *msgo;
- {
- msgo[CBYTE] = (msgo[CBYTE] & ~(MAX_WINDOW << YYYSHIFT))
- | (rseq << YYYSHIFT);
- /* writeframe() will recalculate the xor checksum. */
- }
-
- /*
- * Medium level input routine.
- *
- * Read a packet from the serial port. Handle acknowledgements and
- * retransmissions.
- * Return FAIL if other side went away, SUCCESS if good packet received.
- *
- * With window size of 1, we send a packet and then receive one.
- * FIXME, when we implement a larger window size, this routine will become
- * more complicated and callers will not be able to depend on msgo[]
- * being sent and acknowledged when it returns.
- */
- s_or_f
- inpkt()
- {
- int data, count, need;
- register int i;
- unsigned short pktsum, oursum;
- unsigned char *msgo;
- /*
- * Next vars are for re-queueing received chars to rescan them
- * for a valid packet after an error.
- */
- int queued = -1; /* <0: off, 0: just finished, >0: # chars pending */
- unsigned char *qp;
- static unsigned char qbuf[sizeof msgi]; /* This is static for machines
- where 4K on the stack is too much */
- # define bad(str) { if (DEBUG_LEVEL(6)) printf str; goto oops; }
- if (need_write == NOOP) goto again;
- /* if (need_write == ACK_MSG) goto ack_msg; This doesn't work right */
- /* if (need_write == RETRANS_MSG) goto xmit; */
- xmit:
- /*
- * We have encountered a garbled packet or a timeout, or need an ack.
- *
- * If we have any outstanding unacknowledged data, send the next
- * unack'd packet. If we have no outstanding data, send an RR or
- * an RJ (as decided by ackmsg()).
- */
- if (wseq != ((his_rseq +1) & MAX_WINDOW))
- {
- int tmp_rseq;
- tmp_rseq = his_rseq + 1;
- xmit_again:
- /* msgo = msgout[(tmp_rseq + 1) & MAX_WINDOW]; JIM */
- msgo = msgout[(tmp_rseq) & MAX_WINDOW];
- update_rseq(msgo);
- if (writeframe(pktchksum(msgo, sendbytes)
- /* msgo[CHKBYTE1]|(msgo[CHKBYTE2]>>8) */, msgo)
- != SUCCESS)
- {
- DEBUG(6, "write failed\n", 0);
- return FAIL; /* Write failed */
- }
- tmp_rseq = (tmp_rseq + 1) & MAX_WINDOW;
- if (wseq != tmp_rseq) goto xmit_again;
- }
- else
- ack_msg:
- {
- if (ackmsg() != SUCCESS)
- {
- DEBUG(6, "ack failed\n", 0);
- return FAIL; /* Write failed */
- }
- }
-
- again:
- count = 0;
-
- DEBUG(9, "R ", 0);
-
- while (1) {
- if (queued >= 0) {
- /*
- * Process some stuff from a string.
- * If we just finished the last char queued, and
- * we are still scanning for a DLE, re-xmit our
- * last packet before we continue reading.
- * On the other hand, if we have a valid packet
- * header accumulating, just keep reading the serial
- * port.
- */
- if (--queued < 0) {
- if (count == 0) {
- DEBUG(6, "End queue. Re-xmit.\n", 0);
- goto xmit; /* No packet comin' in */
- } else {
- DEBUG(6,"End queue. Keep reading\n",0);
- goto readser; /* Seems to be sumpin' */
- }
- }
- data = *qp++; /* Just grab from queue */
- } else {
- readser:
- /* FIXME, suck in data in chunks into qbuf */
- data = xgetc();
- if (data == EOF)
- {
- DEBUG(6, "EOF while getc'ing.\n", 0);
- break;
- }
- }
-
- if (DEBUG_LEVEL(9))
- DEBUG_DATA(data);
-
- switch (count)
- {
- case DLEBYTE:
- /* Look for DLE */
- if (data == DLE)
- msgi[count++] = (unsigned char)DLE;
- break;
-
- case LENBYTE:
- /* Check length byte */
- if (data > KCONTROL || data == 0)
- bad(("packet size"));
- if (segbytes[data] > MAX_PACKET) {
- bad(("packet too long for buffer"));
-
- /*
- * Error recovery.
- *
- * First diagnose problem with current pkt.
- * Then scan that packet to see if perhaps
- * we have been looking at garbage and the
- * real packet is "inside" the garbage.
- * If so, rescan from the next DLE. If not,
- * scan further input, or xmit a packet or
- * ack and then handle further input.
- */
- oops:
- if (DEBUG_LEVEL(5))
- printf(": bad packet\n");
-
- if (DEBUG_LEVEL(6) && count > CBYTE) {
- if (tt == CONTROL)
- printf("Bad: CONTROL %s %d\n",
- ctrl_pr[xxx], yyy);
- else
- printf("Bad: %s %d %d\n",
- tt_pr[tt], xxx, yyy);
- }
-
- /* See if any DLEs in the bad packet */
- /* Skip 0, we know that's a DLE */
- for (i = DLEBYTE+1; i < count; i++) {
- if (msgi[i] == DLE) {
- /* Reprocess from the DLE.
- * if queued, back up the q.
- * if not, make one.
- */
- if (queued >= 0) {
- queued += count - i;
- qp -= count - i;
- DEBUG(2,"Backup and rescan queue\n", 0);
- } else {
- bcopy((char *)(msgi+i),
- (char *)qbuf,
- count - i);
- qp = qbuf;
- queued = count - i;
- DEBUG(2,"Queue and rescan input\n", 0);
- }
- goto again;
- }
- }
-
- if (queued >= 0) {
- DEBUG(2, "Continue scan\n", 0);
- goto again;
- } else {
- DEBUG(2, "Re-xmit previous packet\n",0);
- goto xmit; /* Xmit then rcv */
- }
- }
- msgi[count++] = (unsigned char)data; /* Save it */
- if (data > segsiz && data != KCONTROL)
- bad(("packet larger than negotiated"));
- msgsize = segbytes[data]; /* Save Packet size */
- need = FRAMELEN + msgsize;
- break;
-
- case CBYTE:
- /* Break up control byte as well as storing it */
- msgi[count++] = data; /* Save it */
- tt = (data >> TYPESHIFT) & TYPEMASK;
- xxx = (data >> XXXSHIFT ) & XXXMASK;
- yyy = (data >> YYYSHIFT ) & YYYMASK;
-
- /* Now check it a bit */
- switch (tt) { /* Switch on msg type */
- case CONTROL:
- /* Control msg must have KCONTROL size */
- if (msgsize != 0) bad(("K versus Control"));
- break;
-
- case ALTCHN:
- bad(("ALTCHN received")); /* Unsupported */
-
- case SHORTDATA:
- case LONGDATA:
- if (msgsize == 0) bad (("KCONTROL with data"));
- break;
- }
- break;
-
- case FRAMEBYTE:
- /* See whole frame, check it a bit. */
- msgi[count++] = (unsigned char)data;
- if (data != (msgi[1] ^ msgi[2] ^ msgi[3] ^ msgi[4]))
- bad(("frame checksum"));
- pktsum = msgi[CHKBYTE1] + (msgi[CHKBYTE2] << 8);
-
- if (tt == CONTROL) {
- /* Check checksum for control packets */
- oursum = MAGIC - msgi[CBYTE];
- if (pktsum != oursum)
- bad(("control checksum"));
- /*
- * We have a full control packet.
- * Update received seq number for the ones
- * that carry one.
- */
- switch (xxx) {
- /* We don't implement SRJ, nor does Unix */
- case SRJ:
- bad(("SRJ received"));
- case RJ:
- if (((wseq - yyy) & MAX_WINDOW)
- > sendwin) {
- /* Atari ST cpp has problems with macro args broken across lines.
- * That's why funny indent here. */
- bad (("RJ out of window, yyy=%d wseq=%d\n", yyy, wseq));
- }
- his_rseq = (yyy) & MAX_WINDOW;
- need_write = RETRANS_MSG;
- break;
- case RR:
- if (((wseq - yyy) & MAX_WINDOW)
- > sendwin) {
- bad (("RR out of window, yyy=%d wseq=%d\n", yyy, wseq));
- }
- /*
- * This is an experiment. The
- * packet protocol paper claims
- * that "Packets must arrive in
- * sequence number order and are
- * only acknowledged in order."
- */
- if (yyy != his_rseq &&
- yyy != ((his_rseq+1) & MAX_WINDOW)) {
- his_rseq = yyy;
- bad(("RR not sequential, yyy=%d, wseq=%d, his_rseq=%d\n", yyy, wseq, his_rseq));
- }
- his_rseq = yyy;
- /* printf("RR: yyy=%d, wseq=%d\n", (int)yyy, (int)wseq); */
- }
- goto done;
- } else {
- /*
- * Received frame of data packet.
- *
- * Now that the xor checksum has been verified,
- * we can believe the acknowledgement (if
- * any) in it.
- */
- if (((xxx - rseq) & MAX_WINDOW) > wndsiz) {
- bad (("data out of window, xxx=%d rseq=%d", xxx, rseq));
- }
- if (((wseq - yyy) & MAX_WINDOW) > sendwin) {
- bad (("data ack out of window, yyy=%d wseq=%d", yyy, wseq));
- }
- /*
- * This is an experiment. The
- * packet protocol paper claims
- * that "Packets must arrive in
- * sequence number order and are
- * only acknowledged in order."
- */
- if (yyy != his_rseq &&
- yyy != ((his_rseq) & MAX_WINDOW) ) {
- bad(("data ack not sequential, yyy=%d, wseq=%d, his_rseq=%d", yyy, wseq, his_rseq));
- }
- his_rseq = yyy;
- }
- break;
-
- default:
- msgi[count++] = (unsigned char)data;
- if (count >= need) {
- /* We have received a full data packet */
- oursum = pktchksum(msgi, msgsize);
- if (pktsum != oursum) {
- /* Send a reject on this pkt */
- reject = xxx - 1;
- bad(("\ndata checksum in packet %x, ours=%x", pktsum, oursum));
- }
- if (xxx != ((rseq+1) & MAX_WINDOW)) {
- bad(("Not next packet xxx=%d rseq=%d\n", xxx, rseq));
- }
- done:
- if (DEBUG_LEVEL(9))
- putchar('\n');
- if (DEBUG_LEVEL(6)) {
- if (tt == CONTROL)
- {
- if (xxx == CLOSE)
- {
- wseq = (his_rseq + 1) & MAX_WINDOW;
- connection_closing = TRUE;
- }
- printf("Rcvd: CONTROL %s %d\n",
- ctrl_pr[xxx], yyy);
- }
- else
- printf("Rcvd: %s %d %d\n",
- tt_pr[tt], xxx, yyy);
- }
- need_write = ACK_MSG;
- return SUCCESS;
- }
- break;
- }
- }
- DEBUG(6, " EOF\n", 0);
- return FAIL;
- }
-
- /*
- * Calculate the checksum of a packet.
- * This code has been modified to work on systems where bytes are not 8
- * bits and shorts are not 16 bits. *Please* let me know if it does
- * not work on your system, no matter what the byte size.
- *
- * To determine whether this still works with shorts larger than 16 bits,
- * just uncomment the "#define short int" below.
- */
- #define HIGHBIT16 0x8000
- #define JUST16BITS 0xFFFF
- #define JUST8BITS 0x00FF
-
- int
- pktchksum(msg, bytes)
- unsigned char *msg;
- int bytes;
- {
- return (JUST16BITS &
- (MAGIC - (chksum(&msg[6], bytes) ^ (JUST8BITS & msg[4]))));
-
- }
-
-
- int
- chksum(s,n)
- register unsigned char *s;
- register n;
- {
- /* For debugging the checksum on PDP-10s, uncomment the following line
- * and the line at the bottom of the function. */
- /* #define short int */
- register short sum;
- register unsigned short t;
- register short x;
-
- sum = (-1) & JUST16BITS;
- x = 0;
- do {
- /* Rotate "sum" left by 1 bit, in a 16-bit barrel */
- if (sum & HIGHBIT16)
- {
- sum = (1 + (sum << 1)) & JUST16BITS;
- }
- else
- sum <<= 1;
- t = sum;
- sum = (sum + (*s++ & JUST8BITS)) & JUST16BITS;
- x += sum ^ n;
- if ((unsigned short)sum <= t)
- sum = (sum ^ x) & JUST16BITS;
- } while (--n > 0);
-
- return(sum);
- /* End of debugging check */
- /* #undef short */
- }
-
-
- /*
- * Medium level packet driver input routine.
- *
- * Read a data packet from the other side. If called twice in succession,
- * we send an ack of the previous packet. Otherwise we tend to piggyback
- * the acks on data packets.
- *
- * Result is SUCCESS if we got a data packet, FAIL if we got some other kind,
- * or a hangup timeout.
- */
- s_or_f
- indata()
- {
-
- while (1) {
- if (inpkt() != SUCCESS)
- {
- DEBUG(6, "inpkt failing\n", 0);
- return FAIL;
- }
- switch (tt) {
- case ALTCHN:
- DEBUG(6, "ALTCHN unsupported\n", 0);
- return FAIL; /* Unsupported - yet */
-
- case LONGDATA:
- case SHORTDATA:
- /*
- * We got a data packet. That's what we want,
- * so return.
- */
- rseq = xxx; /* Packet correctly received */
- return SUCCESS; /* We are done. */
-
- case CONTROL:
- switch (xxx) {
- default:
- DEBUG(6, "Bad packet type\n", 0);
- return FAIL; /* Bad packet type */
-
- case RJ: /* Reject prev pkt */
- {
- break;
- }
- case RR: /* OK but no data */
- {
- break;
- } /* Ack and try again */
- }
- }
- }
- }
-
-
-
- /*
- * Open a conversation in the g protocol. Medium level routine.
- *
- * We need to both send and receive each packet type in turn.
- * If this sequence falters, we start over at INITA, trying a few more times.
- *
- * Returns SUCCESS for success, FAIL for failure.
- */
- s_or_f
- gturnon(mastermode/* , fd*/)
- int mastermode; /* 1 for master, 0 for slave */
- /* int fd; */ /* File descriptor to operate on */
- {
- int tries = 0;
- int step = 0;
- static int which[] = {INITA, INITA, INITB, INITB, INITC, INITC};
-
- /* initialize protocol globals, e.g. packet sequence numbers */
-
- rseq = 0; /* Last good packet # we have seen from him */
- wseq = 1; /* Next packet # we will send */
- his_rseq = 0; /* Last good Packet # he has seen from us */
- reject = NOREJECT; /* Don't reject first packet */
-
- if (mastermode) goto master_start;
-
- while (++tries <= 8) {
- /* Receive an initialization packet and handle it */
- if (inpkt() == SUCCESS && tt == CONTROL && xxx == which[step]) {
- switch (xxx) {
- /* Remember we've seen it, grab value */
- case INITA:
- do_inita:
- sendwin = yyy;
- DEBUG(6, "INITA SENDWIN: %d\n", sendwin);
- break;
- case INITB:
- /*
- * Get preferred packet size for other guy,
- * but don't overrun our buffer space.
- * The encoded segment size is off-by-1 from
- * the one used in the K field in each packet.
- */
- do {
- sendseg = yyy+1;
- sendbytes = segbytes[sendseg];
- } while (sendbytes > MAX_PACKET && --yyy);
- DEBUG(6, "INITB sendbytes: %d\n", sendbytes);
- break;
- case INITC:
- sendwin = yyy;
- DEBUG(6, "INITC SENDWIN: %d\n", sendwin);
- break;
- }
- if (++step >= 6) /* Move forward */
- return SUCCESS; /* We are done */
- } else {
- /* Start over, sigh */
- step = 0;
- if (xxx == INITA) /* Start with his inita */
- goto do_inita;
- }
-
- master_start:
- /*
- * Transmit an initialization packet.
- */
- switch (which[step]) {
- case INITA:
- ctlmsg((INITA << XXXSHIFT) | wndsiz);
- break;
- case INITB:
- ctlmsg((INITB << XXXSHIFT) | (segsiz - 1));
- break;
- case INITC:
- ctlmsg((INITC << XXXSHIFT) | wndsiz);
- break;
- }
- if (++step >= 6)
- return SUCCESS; /* We are done */
- }
- return FAIL; /* Failure */
- }
-
-
- /*
- * Turn off conversation in the G protocol. Medium level routine.
- */
- s_or_f
- gturnoff()
- {
- int tries = 0;
- # define TURNOFF_TRIES 19
- /* Loop til all prevous data's been ack'd */
- need_write = NOOP;
- while ((wseq != ((his_rseq + 1) & MAX_WINDOW)) ||
- (tt != CONTROL && xxx != CLOSE))
- {
- if (++tries > TURNOFF_TRIES) return FAIL;
- ackmsg();
- if (inpkt() != SUCCESS) return FAIL;
- }
- tries = 0;
- do {
- if (++tries > TURNOFF_TRIES) return FAIL;
- ctlmsg(CLOSE << XXXSHIFT);
- if (inpkt() != SUCCESS)
- return FAIL;
- } while (tt != CONTROL && xxx != CLOSE);
-
- return SUCCESS;
- }
-
-
- /*
- * Read a message from the other side.
- *
- * Messages are always contained in LONGDATA packets. If a message is
- * longer than a single packet, only the last packet will contain a null
- * character. Keep catenating them until you see the null.
- *
- * Return SUCCESS or FAIL. If the received message is longer than our
- * buffer, we eat the whole message, but return FAIL once it ends.
- */
- s_or_f
- grdmsg(str, fn)
- char *str; /* Buffer to put it in, sized MAXMSGLEN */
- int fn; /* File descriptor to read it from */
- {
- unsigned msglen;
- unsigned totlen = 0;
- unsigned oldlen;
-
- str[0] = '\0'; /* No command yet */
- do {
- if (indata() != SUCCESS || tt != LONGDATA) {
- DEBUG(5, "grdmsg failing\n", 0);
- return FAIL;
- }
- msgi[FRAMELEN+msgsize] = '\0'; /* Null terminate packet */
- msglen = strlen((char *)(msgi+6));
- oldlen = totlen;
- totlen += msglen;
-
- /* Tack on to command */
- if (totlen < MAXMSGLEN) {
- strcat(str+oldlen,(char *)&msgi[FRAMELEN]);
- }
- } while (msglen == msgsize); /* Loop if no null in pkt */
-
- if (DEBUG_LEVEL(5) && (totlen >= MAXMSGLEN)) {
- printf("grdmsg: message overflow: size=%d, max=%d\n", totlen,
- MAXMSGLEN);
- }
-
- return (totlen < MAXMSGLEN)? SUCCESS: FAIL;
- }
-
-
- /*
- * Write a message to the other side.
- *
- * We write the first (or only) packet from our local bufr[], the
- * rest comes straight out of the caller's string.
- */
- s_or_f
- gwrmsg(type, str, fn)
- char type;
- char *str;
- int fn;
- {
- char bufr[MAX_PACKET];
- char *ptr;
- int thislen, totlen;
-
- bufr[0] = type;
- totlen = strlen(str) + 1;
- if (totlen > MAXMSGLEN) abort();
- strncpy(&bufr[1], str, sendbytes-1);
- ptr = bufr;
-
- for (;;) {
- thislen = totlen;
- if (thislen > sendbytes) thislen = sendbytes;
- if (sendpacket(ptr, thislen, LONGDATA) != SUCCESS)
- return FAIL;
- totlen -= sendbytes;
- if (totlen < 0) break;
- if (ptr == bufr) ptr = str + (sendbytes - 1);
- else ptr += sendbytes;
- }
-
- return SUCCESS;
- }
-
- /*
- * Write a file to the other side.
- */
- s_or_f
- gwrdata(file, fn)
- FILE *file; /* File to be sent */
- int fn; /* fd of serial line */
- {
- char dskbuf[MAX_PACKET]; /* Disk I/O buffer */
- int count;
-
- do {
- count = fread(dskbuf, sizeof (char), sendbytes, file);
- /* if (count < 0) return FAIL; */ /* FIXME, should send EOF */
- if ferror(file) return FAIL;
- if (sendpacket(dskbuf, count,
- (count == sendbytes)? LONGDATA: SHORTDATA) != SUCCESS)
- return FAIL;
- } while (count);
- need_write = NOOP;
- return SUCCESS;
- }
-
- /*
- * Read a file from the other side.
- */
- s_or_f
- grddata(fn, file)
- int fn; /* File desc of serial line */
- FILE *file; /* stdio file ptr of file to read into */
- {
- int offset;
- int status;
- int error = 0;
-
- do {
- /* Read a packet, handle the data in it */
- if (indata() != SUCCESS)
- return FAIL;
-
- switch (tt) {
- case LONGDATA:
- /* FIXME, check this write */
- ackmsg(); /* Send ack early, before
- disk write */
- offset = FRAMELEN;
- goto writeit;
- case SHORTDATA:
- if (msgi[SHORTLEN1] & 0x80) {
- msgsize -=
- (msgi[SHORTLEN2] << 7) | (127&msgi[SHORTLEN1]);
- offset = FRAMELEN+2;
- } else {
- msgsize -= msgi[SHORTLEN1];
- offset = FRAMELEN+1;
- }
-
- writeit:
- /* FIXME:
- * Consider skipping further writing if error != 0 */
- if (msgsize != 0) {
- status = fwrite((char *) &msgi[offset],
- sizeof (char), msgsize, file);
- if (status != msgsize) error++;
- }
- break;
- }
- } while (msgsize != 0);
-
- return error? FAIL: SUCCESS;
- }
-
-